package com.dianwoba.core.redis.operation.impl;

import com.dianwoba.core.redis.JedisCallback;
import com.dianwoba.core.redis.JedisTemplate;
import com.dianwoba.core.redis.operation.ListOperations;
import redis.clients.jedis.BinaryClient.LIST_POSITION;
import redis.clients.jedis.BinaryJedisCommands;

import java.util.Collection;
import java.util.List;

/**
 * Default implementation of {@link ListOperations}.
 * 
 */
public class DefaultListOperations<K, V, X extends BinaryJedisCommands> 
    extends AbstractOperations<K, V, X>  implements ListOperations<K, V> {

	public DefaultListOperations(JedisTemplate<K, V, X> template) {
		super(template);
	}

	public V index(K key, final long index) {
		return execute(new ValueDeserializingRedisCallback(key) {

			protected byte[] inRedis(byte[] rawKey, X jedis) {
				return jedis.lindex(rawKey, index);
			}
		});
	}

	public V leftPop(K key) {
		return execute(new ValueDeserializingRedisCallback(key) {

			protected byte[] inRedis(byte[] rawKey, X jedis) {
				return jedis.lpop(rawKey);
			}
		});
	}

    @Override
    public V leftPop(K key, Class<V> clazz) {
        return execute(new ValueDeserializingRedisCallback(key, clazz) {

            protected byte[] inRedis(byte[] rawKey, X jedis) {
                return jedis.lpop(rawKey);
            }
        });
    }

    public Long leftPush(K key, V value) {
		final byte[] rawKey = rawKey(key);
		final byte[] rawValue = rawValue(value);
		return execute(new JedisCallback<Long, X>() {

			public Long doInRedis(X jedis) {
				return jedis.lpush(rawKey, rawValue);
			}
		});
	}

	public Long leftPushAll(K key, V... values) {
		final byte[] rawKey = rawKey(key);
		final byte[][] rawValues = rawValues(values);
		return execute(new JedisCallback<Long, X>() {
			public Long doInRedis(X jedis) {
				return jedis.lpush(rawKey, rawValues);
			}
		});
	}

	@Override
	public Long leftPushAll(K key, Collection<V> values) {

		final byte[] rawKey = rawKey(key);
		final byte[][] rawValues = rawValues(values);

		return execute(new JedisCallback<Long, X>() {
			public Long doInRedis(X jedis) {
				return jedis.lpush(rawKey, rawValues);
			}
		});
	}

	public Long leftPushIfPresent(K key, V value) {
		final byte[] rawKey = rawKey(key);
		final byte[] rawValue = rawValue(value);
		return execute(new JedisCallback<Long, X>() {

			public Long doInRedis(X jedis) {
				return jedis.lpushx(rawKey, rawValue);
			}
		});
	}

	public Long leftPush(K key, V pivot, V value) {
		final byte[] rawKey = rawKey(key);
		final byte[] rawPivot = rawValue(pivot);
		final byte[] rawValue = rawValue(value);
		return execute(new JedisCallback<Long, X>() {

			public Long doInRedis(X jedis) {
				return jedis.linsert(rawKey, LIST_POSITION.BEFORE, rawPivot, rawValue);
			}
		});
	}

	public Long size(K key) {
		final byte[] rawKey = rawKey(key);
		return execute(new JedisCallback<Long, X>() {

			public Long doInRedis(X jedis) {
				return jedis.llen(rawKey);
			}
		});
	}

    public List<V> range(K key, final long start, final long end) {
        final byte[] rawKey = rawKey(key);
        return execute(new JedisCallback<List<V>, X>() {
            public List<V> doInRedis(X jedis) {
                return deserializeValues(jedis.lrange(rawKey, start, end));
            }
        });
    }

    @Override
    public List<V> range(K key, final long start, final long end, final Class<V> clazz) {
        final byte[] rawKey = rawKey(key);
        return execute(new JedisCallback<List<V>, X>() {
            public List<V> doInRedis(X jedis) {
                return deserializeValues(jedis.lrange(rawKey, start, end),clazz);
            }
        });
    }

    public Long remove(K key, final long count, Object value) {
		final byte[] rawKey = rawKey(key);
		final byte[] rawValue = rawValue(value);
		return execute(new JedisCallback<Long, X>() {

			public Long doInRedis(X jedis) {
				return jedis.lrem(rawKey, count, rawValue);
			}
		});
	}

	public V rightPop(K key) {
		return execute(new ValueDeserializingRedisCallback(key) {

			protected byte[] inRedis(byte[] rawKey, X jedis) {
				return jedis.rpop(rawKey);
			}
		});
	}

    @Override
    public V rightPop(K key, Class<V> clazz) {
        return execute(new ValueDeserializingRedisCallback(key, clazz) {
            protected byte[] inRedis(byte[] rawKey, X jedis) {
                return jedis.rpop(rawKey);
            }
        });
    }

    public Long rightPush(K key, V value) {
		final byte[] rawKey = rawKey(key);
		final byte[] rawValue = rawValue(value);
		return execute(new JedisCallback<Long, X>() {

			public Long doInRedis(X jedis) {
				return jedis.rpush(rawKey, rawValue);
			}
		});
	}

	public Long rightPushAll(K key, V... values) {
		final byte[] rawKey = rawKey(key);
		final byte[][] rawValues = rawValues(values);
		return execute(new JedisCallback<Long, X>() {
			public Long doInRedis(X jedis) {
				return jedis.rpush(rawKey, rawValues);
			}
		});
	}

	@Override
	public Long rightPushAll(K key, Collection<V> values) {

		final byte[] rawKey = rawKey(key);
		final byte[][] rawValues = rawValues(values);

		return execute(new JedisCallback<Long, X>() {
			public Long doInRedis(X jedis) {
				return jedis.rpush(rawKey, rawValues);
			}
		});
	}

	public Long rightPushIfPresent(K key, V value) {
		final byte[] rawKey = rawKey(key);
		final byte[] rawValue = rawValue(value);
		return execute(new JedisCallback<Long, X>() {

			public Long doInRedis(X jedis) {
				return jedis.rpushx(rawKey, rawValue);
			}
		});
	}

	public Long rightPush(K key, V pivot, V value) {
		final byte[] rawKey = rawKey(key);
		final byte[] rawPivot = rawValue(pivot);
		final byte[] rawValue = rawValue(value);

		return execute(new JedisCallback<Long, X>() {

			public Long doInRedis(X jedis) {
				return jedis.linsert(rawKey, LIST_POSITION.AFTER, rawPivot, rawValue);
			}
		});
	}

	public void set(K key, final long index, V value) {
		final byte[] rawValue = rawValue(value);
		execute(new ValueDeserializingRedisCallback(key) {

			protected byte[] inRedis(byte[] rawKey, X jedis) {
				jedis.lset(rawKey, index, rawValue);
				return null;
			}
		});
	}

	public void trim(K key, final long start, final long end) {
		execute(new ValueDeserializingRedisCallback(key) {

			protected byte[] inRedis(byte[] rawKey, X jedis) {
				jedis.ltrim(rawKey, start, end);
				return null;
			}
		});
	}

}
